我們介紹了 SOLID、clean architecture、dependency injection 之後,相信大家寫 code 的時候,都會多想二分鐘怎麼讓程式碼寫得更有架構吧。
差不多是時候回頭來探討 Android 本身的架構問題了,Android 有很多種不同的架構可適用,不論是 MVP、MVI、MVVM,切多少層或是怎麼切、每層之間怎麼互動都有些許不同,但不論哪種架構本質上都是把邏輯從 UI 層獨立出來,讓每一層可以專注於某一種權責,Android 官方並沒有明確訂定什麼才是標準的做法,但我們可以發現在各個場合,MVVM 這個名字一直不斷的被提出來,我們今天就一起來看看 MVVM 到底有什麼過人之處吧!
大家可以想像一下我們的目標是想要把邏輯從 UI 層抽離。
假設抽離出來的這一層叫做 X ,UI 會直接跟 X 互動,可能是跟 X 拿資料或其他需求,有些情境 X 也會需要跟 UI 互動,比如說 api 資料回傳了要通知 UI 更新。
這樣相互依賴的關係恰恰違反了 clean architecture 裡,外層只會依賴內層這種單一方向的原則。
把 X 跟 UI 互動的介面定義一層 interface 出來,就變成 MVP 架構囉!
而在 MVVM 的架構裡,外層只會依賴內層這種守則是必須被嚴格遵守的。
MVVM stands for Model、View、ViewModel,相信 Model 跟 View 大家都不陌生,而 ViewModel 則是 Android 提供的一套 library ,主要用途是讓我們暫存跟管理讓 UI 層使用的資料,ViewModel 不會知道 UI 層的存在,通常是透過 LiveData 或其他類似機制給 UI 層,讓 UI 層註冊得到資料更新的事件。
ViewModel 的另一個優點是因為 Activity 層常會有螢幕旋轉等事件會重建 Activity,資料必須不斷的儲存再拿出來其實有點麻煩,而透過 ViewModel 可以讓我們安全方便的保存較龐大的資料。
LiveData 是個 lifecycle aware 的 observable 物件。
UI 層透過 LiveData 註冊資料更新的事件通知,當資料有變化的時候就會主動通知註冊的 UI 物件,這種反轉主被動的手法是不是似曾相似呢。
另一個重點是 lifecycle aware,當綁定的 Activity 已經被 destroy 的時候,LiveData 也會自動釋放註冊的物件引用,不會導致潛在的 memory leak 風險。

資料來源:https://developer.android.com/jetpack/docs/guide
MVVM 的架構可以用這張圖來說明,Activity/Fragment 所在的 UI 層只會知道 ViewModel 的存在,而 ViewModel 則依據不同需求依賴不同的 Repository 獲取資料或是進行其他互動,Repository 則可以依據自己的商業邏輯決定從 Retrofit 跟後端要資料或是直接透過 Room 拿本地端的資料回傳給 ViewModel,而 UI 層會直接跟 ViewModel 註冊資料更新的通知。
簡而言之,UI 就是控制畫面,而 ViewModel 就是控制資料,藉此達到 UI 與邏輯的切分。
讓我們一起來看個例子吧:
跟以往一樣我們需要宣告 dependency 在 build.gradle 裡:
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
}
以下是一個簡單的 ViewModel 範例:
class NameViewModel : ViewModel() {
// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
currentName 是一個 MutableLiveData,可以透過 setValue 設值,當值發生變化時,會主動通知已註冊的物件。
而在 Activity 這一層關於 ViewModel 的使用方法如下:
class NameActivity : AppCompatActivity() {
private lateinit var model: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Other code to setup the activity...
// Get the ViewModel.
model = ViewModelProviders.of(this).get(NameViewModel::class.java)
// Create the observer which updates the UI.
val nameObserver = Observer<String> { newName ->
// Update the UI, in this case, a TextView.
nameTextView.text = newName
}
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.currentName.observe(this, nameObserver)
}
}
我們透過 ViewModelProviders.of(this).get(NameViewModel::class.java) 拿到 NameViewModel 的實體,Android 會保證當螢幕旋轉等時候,我們所拿到的 ViewModel 物件會是正確的,透過 observe function 來等待資料的更新,當資料有更新的時候我們就改變我們的畫面。
這樣就達成了 Activity 知道 ViewModel 的存在、 ViewModel 不知道 Activity 的存在,但是 ViewModel 資料的更新,可以安全的透過 LiveData 通知給 Activity。
挑戰:要如何透過 dependency injection 提供
ViewModel呢?
挑戰:試著把
Room的介面改成LiveData,就可以更無縫的接軌ViewModel囉!
參考資料:
https://developer.android.com/topic/libraries/architecture/livedata
https://developer.android.com/topic/libraries/architecture/viewmodel
Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786